#include "dxstdafx.h"
#include "xfileentity.h"
#include "structures.h"
#include "MeshHierarchy.h"
#include "Utilities.h"

// The time to change from one animation set to another
// To see how the merging works - increase this time value to slow it down
const float kMoveTransitionTime=0.25f;

/**
 * \brief Constructor
 * \author Keith Ditchburn \date 17 July 2005
*/
CXFileEntity::CXFileEntity(void)
:	//m_frameRoot(0), m_animController(0), m_numAnimationSets(0),m_currentAnimationSet(0), 
	m_boneMatrices(0),m_maxBones(0),m_sphereCentre(0,0,0),m_sphereRadius(0),
	//m_speedAdjust(1.0f),
	//m_currentTrack(0),m_currentTime(0),
	m_firstMesh(0), m_mxWorld()
{
	// The default world matrix: identity.
	D3DXMatrixIdentity( &m_mxWorld );
}

D3DXMATRIX CXFileEntity::GetWorldTransform()
{
    return m_mxWorld;
}

void CXFileEntity::SetWorldTransform( const D3DXMATRIX * pmxWorld )
{
    assert( pmxWorld != NULL );
    m_mxWorld = * pmxWorld;
}

/**
 * \brief Destructor
 * \author Keith Ditchburn \date 17 July 2005
*/
CXFileEntity::~CXFileEntity(void)
{	 
	if (m_frameRoot)
	{
		// Create a mesh heirarchy class to control the removal of memory for the frame heirarchy
		CMeshHierarchy Alloc;
		D3DXFrameDestroy(m_frameRoot, &Alloc);
		m_frameRoot=NULL;
	}

    SAFE_RELEASE(m_animController);
	SAFE_DELETE_ARRAY(m_boneMatrices);
}

/**
 * \brief Clone function; Returns a new copy of this entity.
 * \param device - the D3D device object.
 * \param cxFileEntity - the pointer we will place the clone within.
 * \return HRESULT indicating success
 * \author Mike Blackney \date 14 Oct 2006
*/
HRESULT CXFileEntity::Clone( LPDIRECT3DDEVICE9 device, CXFileInstance *&fileInstance ) {


	////////////////////////////
	//  THIS FUNCTION SHOULD REPLICATE THE BEHAVIOUR OF
	//  THE FUNCTION CMultiAnim::CreateInstance( )
	///////////////////////////

	fileInstance = NULL;

    LPD3DXANIMATIONCONTROLLER pNewAC = NULL;
    HRESULT hr;
    CXFileInstance * pAI = NULL;

    // Clone the original AC.  This clone is what we will use to animate
    // this mesh; the original never gets used except to clone, since we
    // always need to be able to add another instance at any time.
    hr = m_animController->CloneAnimationController(
			m_animController->GetMaxNumAnimationOutputs(),
			m_animController->GetMaxNumAnimationSets(),
			m_animController->GetMaxNumTracks(),
			m_animController->GetMaxNumEvents(),
			&pNewAC );
    if( SUCCEEDED( hr ) )
    {
        // create the new instance
        pAI = new CXFileInstance( device, this );
        if( pAI == NULL )
        {
            hr = E_OUTOFMEMORY;
            goto e_Exit;
        }

        // set it up
        hr = pAI->Setup( pNewAC );
        if( FAILED( hr ) )
            goto e_Exit;

        fileInstance = pAI;
    }

e_Exit:

    if( FAILED( hr ) )
    {
        if( pAI )
            delete pAI;

        if( pNewAC )
            pNewAC->Release();
    }

    return hr;

	/*
	// We want to ensure that nobody clones clones.  Not for any special reason,
	// except to enforce me to keep track of the original and only clone from it:
	assert(bIsClone == false);

	// We return a fail if we haven't loaded at all yet:
	if (!m_animController)
		return E_FAIL;

	// Allocate new memory for the clone:
	cxFileEntity = new CXFileEntity( );

	// Allocated successfully?
	if ( !cxFileEntity ) {
		return E_FAIL;
	}

	HRESULT hr = S_OK; 

	// Duplicate the complex objects.
	// Clone the animation controller:
	if (m_animController)
		V_RETURN( m_animController->CloneAnimationController(
			m_animController->GetMaxNumAnimationOutputs( ),
			m_animController->GetMaxNumAnimationSets( ),
			m_animController->GetMaxNumTracks( ),
			m_animController->GetMaxNumEvents( ),
			&(cxFileEntity->m_animController) ) );

	// Clone the frames:
	if (m_frameRoot) {
		CloneFrameHierarchy( m_frameRoot, &(cxFileEntity->m_frameRoot) );
	}

	// Set up the bone matrices:
	//cxFileEntity->InitBones( device );

	CLONE_SET(cxFileEntity,m_maxBones);
	CLONE_SET(cxFileEntity,m_sphereCentre);
	CLONE_SET(cxFileEntity,m_sphereRadius);
	CLONE_SET(cxFileEntity,m_currentAnimationSet);	
	CLONE_SET(cxFileEntity,m_numAnimationSets);
	CLONE_SET(cxFileEntity,m_currentTrack);
	CLONE_SET(cxFileEntity,m_currentTime);
	CLONE_SET(cxFileEntity,m_speedAdjust);

	// Ensure that the clone knows that it is actually a clone:
	cxFileEntity->bIsClone = true;

	// Register the animation controller as using the new copy of the frames:
	D3DXFrameRegisterNamedMatrices( cxFileEntity->m_frameRoot, cxFileEntity->m_animController );

	return hr;
	*/
}

/**
 * \brief Clones a frame hierarchy
 * \author Mike Blackney 14 Oct 2006
*/
/*
void CXFileEntity::CloneFrameHierarchy( LPD3DXFRAME pFrameSrc, LPD3DXFRAME* pFrameDest ) const {

	// Clones the hierarchy of a frame.  Used for clones of a .x model loaded
	// from a file.

	if ( pFrameSrc ) {

		(*pFrameDest) = new D3DXFRAME;
		*(D3DXFRAME*)(*pFrameDest) = *(D3DXFRAME*)pFrameSrc;

		if ( pFrameSrc->pMeshContainer ) {
			(*pFrameDest)->pMeshContainer = new D3DXMESHCONTAINER;
			*(D3DXMESHCONTAINER*)(*pFrameDest)->pMeshContainer = *(D3DXMESHCONTAINER*)pFrameSrc->pMeshContainer;
		}
	}
	else {
		(*pFrameDest) = 0;
		return;
	}

	if ( pFrameSrc->pFrameSibling )
		CloneFrameHierarchy(pFrameSrc->pFrameSibling, &(*pFrameDest)->pFrameSibling);
	else
		(*pFrameDest)->pFrameSibling = 0;

	if ( pFrameSrc->pFrameFirstChild )
		CloneFrameHierarchy(pFrameSrc->pFrameFirstChild, &(*pFrameDest)->pFrameFirstChild);	
	else
		(*pFrameDest)->pFrameFirstChild = 0;
}
*/


/**
 * \brief frees up a cloned frame hierarchy
 * \author Mike Blackney 14 Oct 2006
*/
/*
void CXFileEntity::FreeFrameHierarchy(LPD3DXFRAME pFrame) {
	// If we're pointing to NULL, don't bother:
	if (!pFrame)
		return;

	// Clear up the sibling and children nodes, if they exist:
	if ( pFrame->pFrameSibling )
		FreeFrameHierarchy(pFrame->pFrameSibling);
	if ( pFrame->pFrameFirstChild )
		FreeFrameHierarchy(pFrame->pFrameFirstChild);

	// If there's a meshcontainer, clear it as well:
	if ( pFrame->pMeshContainer ) {
		//delete [] ((D3DXMESHCONTAINER*)pFrame->pMeshContainer)->ppBoneMatrixPtrs;
		delete pFrame->pMeshContainer;	
	}
	delete pFrame;
}
*/

/**
 * \brief Loads the x file
 * \param device - the Direct3D device object
 * \param filename - the file to load
 * \return HRESULT indicating success
 * \author Keith Ditchburn \date 17 July 2005
 * \modified Mike Blackney \date 14 Oct 2006
*/
HRESULT CXFileEntity::LoadXFile(LPDIRECT3DDEVICE9 device,const WCHAR* filename)
{
	assert(device);
	assert(filename);

	// Textures for .x files are normally found in the directory the x file is in (or relative to it) so 
	// change the current directory for the life time of this load operation to the directory of the x file
	
	// Remember old
	WCHAR oldCurrentDir[MAX_PATH];
	GetCurrentDirectory(MAX_PATH,oldCurrentDir);

	// Get path from filename
	WCHAR fname[MAX_PATH];WCHAR drv[MAX_PATH];WCHAR dir[MAX_PATH];	
	_wsplitpath(filename,drv,dir,fname,0);

	WCHAR justPath[MAX_PATH];
	StringCchPrintf(justPath,MAX_PATH,L"%s%s",drv,dir);

	// Set current
	SetCurrentDirectory(justPath);

	// We load the frame heirarchy and animation set
	// We must provide a memory allocator (CMeshHeirarchy here) - it must implement callback functions
	// so that this D3DXLoadMeshHierarchyFromX can call them internally during parsing of the x file
	// This call also returns an animation controller

	// Create our mesh hierarchy class to control the allocation of memory
    CMeshHierarchy Alloc;

	HRESULT hr = D3DXLoadMeshHierarchyFromX(filename, D3DXMESH_MANAGED, device, 
		&Alloc, NULL, &m_frameRoot, &m_animController);

	if (FAILED(hr))
		return E_FAIL;

	// if the x file contains any animation remember how many sets there are

	//if(m_animController)
	//	m_numAnimationSets = m_animController->GetMaxNumAnimationSets();

	// Initialise the bones:
	InitBones( device );

	// Put the current directory back to what it was
	SetCurrentDirectory(oldCurrentDir);

	return hr;
}

/**
 * \brief Initialises the bones of a loaded or cloned mesh.
 * \param device - the D3D device we will display the bones on.
 * \return HRESULT indicating success
 * \author Mike Blackney \date 14 Oct 2006
*/
HRESULT CXFileEntity::InitBones( LPDIRECT3DDEVICE9 device ) {
	// Bones for skining
	if(m_frameRoot)
	{
		// Set the bones up
		SetupBoneMatrices(device,(D3DXFRAME_EXTENDED*)m_frameRoot, NULL);

		// Create the bone matrices array for use during FrameMove to hold the final transform
		m_boneMatrices  = new D3DXMATRIX[m_maxBones];
		ZeroMemory(m_boneMatrices, sizeof(D3DXMATRIX)*m_maxBones);

		// Calculate the Bounding Sphere for this model (used later to position camera correctly)
		D3DXFrameCalculateBoundingSphere(m_frameRoot, &m_sphereCentre, &m_sphereRadius);
	}

	return S_OK;
}

/**
 * \brief we need to go through the hierarchy and set the combined matrices
 * calls itself recursively as it tareverses the hierarchy
 * \param device - the Direct3D device object
 * \param pFrame - current frame
 * \param pParentMatrix - the parent frame matrix
 * \author Keith Ditchburn \date 18 July 2005
*/
void CXFileEntity::SetupBoneMatrices(LPDIRECT3DDEVICE9 device, 
									 D3DXFRAME_EXTENDED *pFrame, LPD3DXMATRIX pParentMatrix)
{
	// Cast to our extended structure first
	D3DXMESHCONTAINER_EXTENDED* pMesh = (D3DXMESHCONTAINER_EXTENDED*)pFrame->pMeshContainer;

	// If this frame has a mesh
	if(pMesh)
	{
		// We need to remember which is the first mesh in the hierarchy for later when we 
		// update (FrameMove)
		if(!m_firstMesh)
			m_firstMesh = pMesh;
		
		// if there is skin info, then setup the bone matrices
		if(pMesh->pSkinInfo && pMesh->MeshData.pMesh)
		{
			// Create a copy of the mesh to skin into later

	// FIX: 19 September 2005 - the CloneMeshFVF method was failing with some .x exported files from some packages
	// due to the FVF not being able to be mapped. Changing to GetDeclaration solves the problem.

			D3DVERTEXELEMENT9 Declaration[MAX_FVF_DECL_SIZE];
			if (FAILED(pMesh->MeshData.pMesh->GetDeclaration(Declaration)))
				return;

			//pMesh->MeshData.pMesh->CloneMeshFVF(D3DXMESH_MANAGED, 
			//	pMesh->MeshData.pMesh->GetFVF(), device, 
			//	&pMesh->exSkinMesh);
			pMesh->MeshData.pMesh->CloneMesh(D3DXMESH_MANAGED, 
				Declaration, device, 
				&pMesh->exSkinMesh);

			// Max bones is calculated for later use (to know how big to make the bone matrices array)
			m_maxBones=max(m_maxBones,pMesh->pSkinInfo->GetNumBones());

			// For each bone work out its matrix
			for (UINT i = 0; i < pMesh->pSkinInfo->GetNumBones(); i++)
			{   
				// Find the frame containing the bone
				D3DXFRAME_EXTENDED* pTempFrame = (D3DXFRAME_EXTENDED*)D3DXFrameFind(m_frameRoot, 
						pMesh->pSkinInfo->GetBoneName(i));

				// set the bone part - point is at the transformation matrix
				pMesh->exFrameCombinedMatrixPointer[i] = &pTempFrame->exCombinedTransformationMatrix;
			}

		}
	}

	// Pass on to sibblings
	if(pFrame->pFrameSibling)
		SetupBoneMatrices(device,(D3DXFRAME_EXTENDED*)pFrame->pFrameSibling, pParentMatrix);

	// Pass on to children
	if(pFrame->pFrameFirstChild)
		SetupBoneMatrices(device,(D3DXFRAME_EXTENDED*)pFrame->pFrameFirstChild, &pFrame->exCombinedTransformationMatrix);
}



//=============================
// class CXFileInstance
// this class represents an instance of a .x model loaded from file.
// Based heavily on the CAnimInstance class from the DirectX sample
// MultiAnimation.  This version has been customised by Mike Blackney
// to be used with the rest of the CXFileEntity class, as downloaded
// from toymaker.info (and created by Keith Ditchburn)
//=============================

HRESULT CXFileInstance::Setup( LPD3DXANIMATIONCONTROLLER pAC )
{
    assert( pAC != NULL );

    DWORD i, dwTracks;

    m_pAC = pAC;

	m_numAnimationSets = m_pAC->GetMaxNumAnimationSets();

	SetAnimationSet(0);

    // Start with all tracks disabled
    dwTracks = m_pAC->GetMaxNumTracks();
    for( i = 0; i < dwTracks; ++ i )
        m_pAC->SetTrackEnable( i, FALSE );

    return S_OK;
}

void CXFileInstance::UpdateFrameMatrices(const D3DXFRAME *frameBase, const D3DXMATRIX *parentMatrix, bool bUpdate)
{
    D3DXFRAME_EXTENDED *currentFrame = (D3DXFRAME_EXTENDED*)frameBase;

	D3DXMATRIX *multiplyFrame, *sendFrame;
	// If we're updating the current matrices, set the multiply to be the current matrix:
	if (bUpdate) {
		multiplyFrame = &currentFrame->exCombinedTransformationMatrix;
		sendFrame = (D3DXMATRIX*)parentMatrix;
	}
	// otherwise we're using the original animated matrix:
	else {
		multiplyFrame = &currentFrame->TransformationMatrix;
		sendFrame = &currentFrame->exCombinedTransformationMatrix;
	}

	// If parent matrix exists multiply our frame matrix by it
    if (parentMatrix != NULL)
        D3DXMatrixMultiply(&currentFrame->exCombinedTransformationMatrix, multiplyFrame, parentMatrix);
    else
        currentFrame->exCombinedTransformationMatrix = *multiplyFrame;

	// If we have a sibling recurse 
    if (currentFrame->pFrameSibling != NULL)
        UpdateFrameMatrices(currentFrame->pFrameSibling, parentMatrix, bUpdate);

	// If we have a child recurse 
    if (currentFrame->pFrameFirstChild != NULL)
        UpdateFrameMatrices(currentFrame->pFrameFirstChild, sendFrame, bUpdate);
}

/*
void CXFileInstance::UpdateFrames( D3DXFRAME_EXTENDED * pFrame, D3DXMATRIX * pmxBase )
{
    assert( pFrame != NULL );
    assert( pmxBase != NULL );

    // transform the bone
    D3DXMatrixMultiply( &pFrame->TransformationMatrix,
                        &pFrame->TransformationMatrix,
                        pmxBase );

    // transform siblings by the same matrix
    if( pFrame->pFrameSibling )
        UpdateFrames( (D3DXFRAME_EXTENDED *) pFrame->pFrameSibling, pmxBase );

    // transform children by the transformed matrix - hierarchical transformation
    if( pFrame->pFrameFirstChild )
        UpdateFrames( (D3DXFRAME_EXTENDED *) pFrame->pFrameFirstChild,
                      &pFrame->TransformationMatrix );
}
*/

void CXFileInstance::DrawFrame(LPD3DXFRAME frame) const {
	// Draw all mesh containers in this frame
    LPD3DXMESHCONTAINER meshContainer = frame->pMeshContainer;
    while (meshContainer)
    {
        DrawMeshContainer(meshContainer, frame);
        meshContainer = meshContainer->pNextMeshContainer;
    }

	// Recurse for sibblings
    if (frame->pFrameSibling != NULL)
        DrawFrame(frame->pFrameSibling);

    // Recurse for children
	if (frame->pFrameFirstChild != NULL)
        DrawFrame(frame->pFrameFirstChild);

/*
	if( pFrame->pMeshContainer )
        DrawMeshFrame( pFrame );

    if( pFrame->pFrameSibling )
        DrawFrames( (D3DXFRAME_EXTENDED *) pFrame->pFrameSibling );

    if( pFrame->pFrameFirstChild )
        DrawFrames( (D3DXFRAME_EXTENDED *) pFrame->pFrameFirstChild );
*/
}

void CXFileInstance::DrawMeshContainer(LPD3DXMESHCONTAINER meshContainerBase, LPD3DXFRAME frameBase) const {

	////////////////////
	// THIS FUNCTION MUST BE REPLACED BY
	// A VERSION FROM CXFileEntity, MOST LIKELY:
	//                          CXFileEntity::DrawMeshContainer 
	///////////////////

	assert(m_pDevice);

	// Cast to our extended frame type
	D3DXFRAME_EXTENDED *frame = (D3DXFRAME_EXTENDED*)frameBase;		

	// Cast to our extended mesh container
	D3DXMESHCONTAINER_EXTENDED *meshContainer = (D3DXMESHCONTAINER_EXTENDED*)meshContainerBase;
	
	// Set the world transform
    m_pDevice->SetTransform(D3DTS_WORLD, &frame->exCombinedTransformationMatrix);

	// Loop through all the materials in the mesh rendering each subset
    for (UINT iMaterial = 0; iMaterial < meshContainer->NumMaterials; iMaterial++)
    {
		// use the material in our extended data rather than the one in meshContainer->pMaterials[iMaterial].MatD3D
		m_pDevice->SetMaterial( &meshContainer->exMaterials[iMaterial] );
		m_pDevice->SetTexture( 0, meshContainer->exTextures[iMaterial] );

		// Select the mesh to draw, if there is skin then use the skinned mesh else the normal one
		LPD3DXMESH pDrawMesh = (meshContainer->pSkinInfo) ? meshContainer->exSkinMesh: meshContainer->MeshData.pMesh;

		// Finally Call the mesh draw function
        pDrawMesh->DrawSubset(iMaterial);
    }
/*
    D3DXMESHCONTAINER_EXTENDED * pMC = (D3DXMESHCONTAINER_EXTENDED *) pFrame->pMeshContainer;
    D3DXMATRIX mx;

    if( pMC->pSkinInfo == NULL )
        return;

    // get bone combinations
    LPD3DXBONECOMBINATION pBC = ( LPD3DXBONECOMBINATION )( pMC->m_pBufBoneCombos->GetBufferPointer() );
    DWORD dwAttrib, dwPalEntry;

    // for each palette
    for( dwAttrib = 0; dwAttrib < pMC->m_dwNumAttrGroups; ++ dwAttrib )
    {
        // set each transform into the palette
        for( dwPalEntry = 0; dwPalEntry < pMC->m_dwNumPaletteEntries; ++ dwPalEntry )
        {
            DWORD dwMatrixIndex = pBC[ dwAttrib ].BoneId[ dwPalEntry ];
            if( dwMatrixIndex != UINT_MAX )
                D3DXMatrixMultiply( &m_pFileEntity->m_amxWorkingPalette[ dwPalEntry ],
                                    &( pMC->m_amxBoneOffsets[ dwMatrixIndex ] ),
                                    pMC->m_apmxBonePointers[ dwMatrixIndex ] );
        }

        // set the matrix palette into the effect
        m_pFileEntity->m_pEffect->SetMatrixArray( "amPalette",
                                                 m_pFileEntity->m_amxWorkingPalette,
                                                 pMC->m_dwNumPaletteEntries );

        // we're pretty much ignoring the materials we got from the x-file; just set
        // the texture here
        m_pFileEntity->m_pEffect->SetTexture( "g_txScene", pMC->m_apTextures[ pBC[ dwAttrib ].AttribId ] );

        // set the current number of bones; this tells the effect which shader to use
        m_pFileEntity->m_pEffect->SetInt( "CurNumBones", pMC->m_dwMaxNumFaceInfls - 1 );

        // set the technique we use to draw
        if( FAILED( m_pFileEntity->m_pEffect->SetTechnique( m_pFileEntity->m_sTechnique ) ) )
            return;

        UINT uiPasses, uiPass;

        // run through each pass and draw
        m_pFileEntity->m_pEffect->Begin( & uiPasses, 0 );
        for( uiPass = 0; uiPass < uiPasses; ++ uiPass )
        {
            m_pFileEntity->m_pEffect->BeginPass( uiPass );
            pMC->m_pWorkingMesh->DrawSubset( dwAttrib );
            m_pFileEntity->m_pEffect->EndPass();
        }
        m_pFileEntity->m_pEffect->End();
    }
	*/
}

CXFileInstance::CXFileInstance( LPDIRECT3DDEVICE9 pDevice, CXFileEntity * pFileEntity )
:   m_pDevice( pDevice ),
	m_pFileEntity( pFileEntity ),
    m_pAC( NULL ),
	m_numAnimationSets(0),m_currentAnimationSet(0), 
	m_speedAdjust(1.0f),
	m_currentTrack(0),m_currentTime(0) {

    assert( pFileEntity != NULL );
	assert( pDevice != NULL );

	// The default world matrix: identity.
	D3DXMatrixIdentity( &m_mxWorld );
}


CXFileInstance::~CXFileInstance()
{
}


void CXFileInstance::GetAnimController( LPD3DXANIMATIONCONTROLLER * ppAC )
{
    assert( ppAC != NULL );
    m_pAC->AddRef();
    * ppAC = m_pAC;
}

CXFileEntity * CXFileInstance::GetFileEntity()
{
    return m_pFileEntity;
}

void CXFileInstance::Cleanup()
{
    if( m_pAC )
        m_pAC->Release();
}

HRESULT CXFileInstance::ResetTime()
{
    return m_pAC->ResetTime();
}


HRESULT CXFileInstance::Render3d()
{
	assert(m_pDevice);

	if (m_pFileEntity->m_frameRoot)
		DrawFrame(m_pFileEntity->m_frameRoot);

	return S_OK;
}


void CXFileInstance::FrameMove(float elapsedTime,const D3DXMATRIX *matWorld)
{
	D3DXMATRIX matrix_world;

	// -- Get our complete transform matrix...
	D3DXMatrixIdentity( &matrix_world );
	// The one supplied here is TRANSLATE
	D3DXMatrixMultiply(&matrix_world, matWorld, &matrix_world );
	// The one from FileInstance is ROTATE
	// COMMENTED OUT UNTIL THE PROBLEM WITH SPINNING MESHES IS SOLVED!
	D3DXMatrixMultiply(&matrix_world, &m_mxWorld, &matrix_world );
	// The one from FileEntity is SCALE
	D3DXMatrixMultiply(&matrix_world, &m_pFileEntity->m_mxWorld, &matrix_world );

	// Adjust animation speed
	elapsedTime/=m_speedAdjust;

	// Advance the time and set in the controller
	//if (m_pAC != NULL)
	assert(m_pAC != NULL);
    m_pAC->AdvanceTime(elapsedTime, NULL);

	m_currentTime+=elapsedTime;

	// Now update the model matrices in the hierarchy
	UpdateFrameMatrices(m_pFileEntity->m_frameRoot, &matrix_world);
	//UpdateFrameMatrices(m_pFileEntity->m_frameRoot, matWorld, true);

	// If the model contains a skinned mesh update the vertices
	D3DXMESHCONTAINER_EXTENDED* pMesh = m_pFileEntity->m_firstMesh;
	if(pMesh && pMesh->pSkinInfo)
	{
		UINT Bones = pMesh->pSkinInfo->GetNumBones();

		// Create the bone matrices that transform each bone from bone space into character space
		// (via exFrameCombinedMatrixPointer) and also wraps the mesh around the bones using the bone offsets
		// in exBoneOffsetsArray
		for (UINT i = 0; i < Bones; ++i)
			D3DXMatrixMultiply(&m_pFileEntity->m_boneMatrices[i],&pMesh->exBoneOffsets[i], pMesh->exFrameCombinedMatrixPointer[i]);

		// We need to modify the vertex positions based on the new bone matrices. This is achieved
		// by locking the vertex buffers and then calling UpdateSkinnedMesh. UpdateSkinnedMesh takes the
		// original vertex data (in pMesh->MeshData.pMesh), applies the matrices and writes the new vertices
		// out to skin mesh (pMesh->exSkinMesh). 

		// UpdateSkinnedMesh uses software skinning and so this is the slowest way of carrying out skinning 
		// but is easiest to describe and works on the majority of graphic devices. 
		// Other methods exist that use hardware to do this skinning - see the notes and the 
		// SDK skinned mesh sample for details

		HRESULT hr=S_OK;

		void *srcPtr;
		V(pMesh->MeshData.pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&srcPtr));

		void *destPtr;
		V(pMesh->exSkinMesh->LockVertexBuffer(0, (void**)&destPtr));

		// Update the skinned mesh 
		V(pMesh->pSkinInfo->UpdateSkinnedMesh(m_pFileEntity->m_boneMatrices, NULL, srcPtr, destPtr));

		// Unlock the meshes vertex buffers
		V(pMesh->exSkinMesh->UnlockVertexBuffer());
		V(pMesh->MeshData.pMesh->UnlockVertexBuffer());
	}
}



/**
 * \brief Change to a different animation set
 * Handles transitions between animations to make it smooth and not a sudden jerk to a new position
 * \param index - new animation set index
 * \author Keith Ditchburn \date 18 July 2005
*/
void CXFileInstance::SetAnimationSet(UINT index)
{
	if (index>=m_numAnimationSets || index==m_currentAnimationSet)
		return;

	// Remember current animation
	m_currentAnimationSet=index;

	// Get the animation set from the controller
	LPD3DXANIMATIONSET set;
	m_pAC->GetAnimationSet(m_currentAnimationSet, &set );	

	// Note: for a smooth transition between animation sets we can use two tracks and assign the new set to the track
	// not currently playing then insert Keys into the KeyTrack to do the transition between the tracks
	// tracks can be mixed together so we can gradually change into the new animation

	// Alternate tracks
	DWORD newTrack = ( m_currentTrack == 0 ? 1 : 0 );

	// Assign to our track
	m_pAC->SetTrackAnimationSet( newTrack, set );
    set->Release();	

	// Clear any track events currently assigned to our two tracks
	m_pAC->UnkeyAllTrackEvents( m_currentTrack );
    m_pAC->UnkeyAllTrackEvents( newTrack );

	// Add an event key to disable the currently playing track kMoveTransitionTime seconds in the future
    m_pAC->KeyTrackEnable( m_currentTrack, FALSE, m_currentTime + kMoveTransitionTime );
	// Add an event key to change the speed right away so the animation completes in kMoveTransitionTime seconds
    m_pAC->KeyTrackSpeed( m_currentTrack, 0.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );
	// Add an event to change the weighting of the current track (the effect it has blended with the secon track)
    m_pAC->KeyTrackWeight( m_currentTrack, 0.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );

	// Enable the new track
    m_pAC->SetTrackEnable( newTrack, TRUE );
	// Add an event key to set the speed of the track
    m_pAC->KeyTrackSpeed( newTrack, 1.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );
	// Add an event to change the weighting of the current track (the effect it has blended with the first track)
	// As you can see this will go from 0 effect to total effect(1.0f) in kMoveTransitionTime seconds and the first track goes from 
	// total to 0.0f in the same time.
    m_pAC->KeyTrackWeight( newTrack, 1.0f, m_currentTime, kMoveTransitionTime, D3DXTRANSITION_LINEAR );

	// Remember current track
    m_currentTrack = newTrack;
}

/**
 * \brief Go to the next animation
 * \author Keith Ditchburn \date 18 July 2005
*/
void CXFileInstance::NextAnimation()
{	
	UINT newAnimationSet=m_currentAnimationSet+1;
	if (newAnimationSet>=m_numAnimationSets)
		newAnimationSet=0;

	SetAnimationSet(newAnimationSet);
}

/**
 * \brief Get the name of the animation
 * \param index - the animation set index
 * \return the name
 * \author Keith Ditchburn \date 18 July 2005
*/
const char *CXFileInstance::GetAnimationSetName(UINT index)
{
	if (index>=m_numAnimationSets)
		return NULL;

	// Get the animation set
	LPD3DXANIMATIONSET set;
	m_pAC->GetAnimationSet(m_currentAnimationSet, &set );

	return set->GetName();
}

/**
 * \brief Slow down animation
 * \author Keith Ditchburn \date 18 July 2005
*/
void CXFileInstance::AnimateSlower()
{
	m_speedAdjust+=0.1f;
}

/**
 * \brief Speed up animation
 * \author Keith Ditchburn \date 18 July 2005
*/
void CXFileInstance::AnimateFaster()
{
	if (m_speedAdjust>0.1f)
		m_speedAdjust-=0.1f;
}

D3DXMATRIX CXFileInstance::GetWorldTransform()
{
    return m_mxWorld;
}

void CXFileInstance::SetWorldTransform( const D3DXMATRIX * pmxWorld )
{
    assert( pmxWorld != NULL );
    m_mxWorld = * pmxWorld;
}
